#ifndef _GWBASE_TEST_HARNESS_H_
#define _GWBASE_TEST_HARNESS_H_

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <string>
#include <sstream>
#include "gwbase/base/logging.h"

namespace dicom {

extern void RunAllTests();

class Assertion {
 public:
  Assertion(const SourceLocation& loc)
      : loc_(loc), ok_(true) 
  { 
  }

  ~Assertion() {
    if (!ok_) {
      fprintf(stderr, "%s:%d: %s\n", loc_.file(), loc_.line(),
              os_.str().c_str());
      exit(1);
    }
  }

  Assertion& IsTrue(bool result, const char *msg) {
    if (!result) {
      os_ << "Assertion Failure: " << msg;
      ok_ = false;
    }
    return *this;
  };

#define BIN_OP(name, op)  \
  template <class X, class Y> \
Assertion& name(const X& x, const Y& y) { \
  if (!(x op y)) { \
    os_ << "failed: " << x << ' ' << #op << ' ' << y; \
    ok_ = false; \
  } \
  return *this; \
} \

BIN_OP(IsEq, ==)
BIN_OP(IsNe, !=)
BIN_OP(IsLe, <=)
BIN_OP(IsLt, <)
BIN_OP(IsGe, >=)
BIN_OP(IsGt, >)
#undef BIN_OP

  template <class T>
  Assertion& operator<<(const T& t) {
    os_ << t;
    return *this;
  }

 private:
  SourceLocation loc_;
  bool ok_;
  std::ostringstream  os_;
};

#define EXPECT_TRUE(a) dicom::Assertion(HERE).IsTrue(a, #a) 
#define EXPECT_Eq(a, b) dicom::Assertion(HERE).IsEq(a, b)
#define EXPECT_Ne(a, b) dicom::Assertion(HERE).IsNe(a, b)
#define EXPECT_Le(a, b) dicom::Assertion(HERE).IsLe(a, b)
#define EXPECT_Lt(a, b) dicom::Assertion(HERE).IsLt(a, b)
#define EXPECT_Ge(a, b) dicom::Assertion(HERE).IsGe(a, b)
#define EXPECT_Gt(a, b) dicom::Assertion(HERE).IsGt(a, b)

#define JOIN1(a, b) a##b
#define JOIN(a, b) JOIN1(a, b)

#define TEST(basecls, name)                                                \
class JOIN(_TEST_, name): public basecls {                                 \
 public:                                                                   \
  JOIN(_TEST_, name)(): basecls() { }                                      \
  static void _RunTest() {                                                 \
    JOIN(_TEST_, name) t;                                                  \
    t._Run();                                                              \
  }                                                                        \
  void _Run();                                                             \
};                                                                         \
const bool JOIN(_TEST_ignored, name) =                                     \
  dicom::RegisterTest(#basecls, #name, JOIN(_TEST_, name)::_RunTest);\
void JOIN(_TEST_, name)::_Run()                                

typedef void(*TestFunc)(void);
extern bool RegisterTest(const char* test, const char* base, void(*func)());

}

#endif /* _GWBASE_TEST_HARNESS_H_ */
